package cn.dansj.common.utils.spring.request;

import cn.dansj.common.utils.bean.ResponseMessage;
import cn.dansj.common.utils.enums.ResponseCode;
import cn.dansj.common.utils.http.URLUtils;
import cn.dansj.common.utils.json.JSONUtils;
import cn.dansj.common.utils.json.JSONObject;
import cn.dansj.common.utils.spring.request.servlet.ReadableHttpServletRequest;
import cn.dansj.common.utils.spring.request.servlet.NonHttpServletRequest;
import cn.dansj.common.utils.spring.request.servlet.NonHttpServletResponse;
import cn.dansj.common.utils.transfer.Transformation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 手动管理上下文
 * <br>
 * <code>// 捕获当前上下文
 * <br>
 * String contextId = RequestUtils.captureContext();
 * <br>
 * // 在另一个线程中应用上下文
 * <br>
 * new Thread(() -> {
 * <br>
 * &nbsp;&nbsp;try {
 * <br>
 * &nbsp;&nbsp;&nbsp;&nbsp;RequestUtils.applyContext(contextId);
 * <br>
 * &nbsp;&nbsp;&nbsp;&nbsp;// 访问请求上下文
 * <br>
 * &nbsp;&nbsp;&nbsp;&nbsp;String url = RequestUtils.getFullRequestURL();
 * <br>
 * &nbsp;&nbsp;&nbsp;&nbsp;System.out.println("Accessed from other thread: " + url);
 * <br>
 * &nbsp;&nbsp;} finally {
 * <br>
 * &nbsp;&nbsp;&nbsp;&nbsp;RequestUtils.releaseContext(contextId);
 * <br>
 * &nbsp;&nbsp;}
 * <br>
 * }).start();</code>
 */
public abstract class RequestUtils {
    private static final Logger logger = LoggerFactory.getLogger(RequestUtils.class);
    //跨域有效期
    public static final int CorsExpireSeconds = 3600 * 12;

    // 使用 ThreadLocal 存储请求上下文（线程安全）
    private static final ThreadLocal<RequestContext> CONTEXT_HOLDER = ThreadLocal.withInitial(RequestContext::new);

    // 用于存储请求上下文副本（用于异步任务）
    private static final ConcurrentHashMap<String, RequestContext> CONTEXT_STORE = new ConcurrentHashMap<>();

    private static final AtomicInteger CONTEXT_ID_GENERATOR = new AtomicInteger();

    // 请求上下文封装类
    private static class RequestContext {
        boolean inWebRequest;
        RequestAttributes requestAttributes;

        RequestContext() {
            this.inWebRequest = false;
            this.requestAttributes = null;
        }

        RequestContext(boolean inWebRequest, RequestAttributes attributes) {
            this.inWebRequest = inWebRequest;
            this.requestAttributes = attributes;
        }

        RequestContext copy() {
            return new RequestContext(this.inWebRequest, this.requestAttributes);
        }
    }

    private static RequestContext getContext() {
        return CONTEXT_HOLDER.get();
    }

    public static boolean checkInWebRequest() {
        return getContext().inWebRequest;
    }

    public static HttpServletRequest getRequest() {
        return checkInWebRequest() ? ((ServletRequestAttributes) getContext().requestAttributes).getRequest() : NonHttpServletRequest.INSTANCE;
    }

    public static ServletRequest setRequestAttributesRefreshServletRequest(ServletRequest servletRequest, ServletResponse servletResponse) {
        RequestContext context = getContext();

        if (servletRequest instanceof HttpServletRequest) {
            context.inWebRequest = true;
            HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
            HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
            // 创建新的上下文副本
            RequestAttributes contextCopy = new ServletRequestAttributes(httpServletRequest, httpServletResponse);
            // 设置到Spring的RequestContextHolder
            RequestContextHolder.setRequestAttributes(contextCopy, true);
            // 设置到ThreadLocal
            context.requestAttributes = contextCopy;
            servletRequest = RequestUtils.getReadableHttpServletRequest();
        }
        return servletRequest;
    }

    public static void resetRequestAttributes() {
        RequestContext context = getContext();
        context.inWebRequest = false;
        context.requestAttributes = null;
        RequestContextHolder.resetRequestAttributes();
    }

    public static ReadableHttpServletRequest getReadableHttpServletRequest() {
        final HttpServletRequest request = getRequest();
        return request instanceof ReadableHttpServletRequest ? (ReadableHttpServletRequest) request : new ReadableHttpServletRequest(request);
    }

    public static HttpServletResponse getResponse() {
        return checkInWebRequest() ? ((ServletRequestAttributes) getContext().requestAttributes).getResponse() : NonHttpServletResponse.INSTANCE;
    }

    public static String getRequestParam(String param) {
        return getRequestParams().getString(param);
    }

    public static int getRequestParamToInt(String param) {
        return getRequestParams().getIntValue(param);
    }

    public static String getRequestParam() {
        return getReadableHttpServletRequest().getParam();
    }

    public static JSONObject getRequestParams() {
        return JSONUtils.toJSONObject(getRequestParam());
    }

    public static JSONObject getMaybeMultipartRequestParams() {
        return getRequestParams().putAll(getReadableHttpServletRequest().getMultipartFile());
    }

    public static String getFullRequestURL() {
        return getReadableHttpServletRequest().getRequestURL() + URLUtils.joinParams(RequestUtils.getRequestParams());
    }

    public static String getRealIp() {
        HttpServletRequest request = getRequest();
        return Transformation.nvl(request.getHeader("X-Real-IP"),
                request.getHeader("X-Forwarded-For"),
                request.getHeader("Proxy-Client-IP"),
                request.getHeader("WL-Proxy-Client-IP"),
                request.getRemoteAddr());
    }

    public static void transferUrlToResponse(String fileAddress, String filename) {
        try {
            URL url = new URL(fileAddress);
            URLConnection conn = url.openConnection();
            InputStream inputStream = conn.getInputStream();
            final HttpServletResponse response = RequestUtils.getResponse();
            response.reset();
            response.setContentType(conn.getContentType());
            response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + URLEncoder.encode(filename, StandardCharsets.UTF_8.name()));
            byte[] buffer = new byte[1024];
            int len;
            OutputStream outputStream = response.getOutputStream();
            while ((len = inputStream.read(buffer)) > 0) {
                outputStream.write(buffer, 0, len);
            }
            inputStream.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static String captureContext() {
        RequestContext current = getContext();
        if (!current.inWebRequest) return null;
        // 创建唯一的上下文ID
        String contextId = "ctx-" + CONTEXT_ID_GENERATOR.incrementAndGet();
        // 存储上下文副本
        CONTEXT_STORE.put(contextId, current.copy());
        return contextId;
    }

    public static void applyContext(String contextId) {
        if (contextId == null) return;
        RequestContext snapshot = CONTEXT_STORE.get(contextId);
        if (snapshot != null) {
            RequestContext current = getContext();
            // 设置当前线程上下文
            current.inWebRequest = snapshot.inWebRequest;
            current.requestAttributes = snapshot.requestAttributes;
            // 设置到Spring的RequestContextHolder
            if (snapshot.requestAttributes != null) {
                RequestContextHolder.setRequestAttributes(snapshot.requestAttributes, true);
            }
        }
    }

    public static void releaseContext(String contextId) {
        if (contextId != null) CONTEXT_STORE.remove(contextId);
    }

    public static void runInContext(String contextId, Runnable task) {
        // 保存当前线程的原始上下文
        RequestContext original = getContext().copy();
        try {
            // 应用新的上下文
            applyContext(contextId);
            // 执行任务
            task.run();
        } finally {
            // 恢复原始上下文
            getContext().inWebRequest = original.inWebRequest;
            getContext().requestAttributes = original.requestAttributes;
            RequestContextHolder.setRequestAttributes(original.requestAttributes, true);
        }
    }

    public static Runnable wrap(Runnable task) {
        // 捕获当前上下文
        String contextId = captureContext();
        return () -> {
            try {
                if (contextId != null) {
                    runInContext(contextId, task);
                    releaseContext(contextId);
                } else {
                    task.run();
                }
            } catch (Throwable e) {
                logger.error("线程任务执行失败:", e);
            }
        };
    }

    public static boolean allowCorsCrossOptions() {
        if (!checkInWebRequest()) return false;
        final HttpServletResponse response = getResponse();
        response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
        response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "GET, POST, PUT, DELETE, OPTIONS");
        response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "*");
        response.setHeader(HttpHeaders.ACCESS_CONTROL_MAX_AGE, String.valueOf(CorsExpireSeconds));
        response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
        response.setHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "*");

        //预检请求
        if (getRequest().getMethod().equals(HttpMethod.OPTIONS.name())) {
            response.setStatus(HttpServletResponse.SC_OK);
            return true;
        }
        return false;
    }

    private static final InheritableThreadLocal<ResponseMessage> responseMessageThreadLocal = new InheritableThreadLocal<>();

    public static ResponseMessage getResponseMessage() {
        final ResponseMessage responseMessage = responseMessageThreadLocal.get();
        responseMessageThreadLocal.remove();
        return responseMessage;
    }

    public static void clearResponseMessage() {
        responseMessageThreadLocal.remove();
    }

    public static void setResponseMessage(int status, String message) {
        responseMessageThreadLocal.set(ResponseMessage.getInstance().responseCode(status).message(message));
    }

    public static <T> T response404() {
        return response(404, "PageNotFound");
    }

    public static <T> T response(int status, String message) {
        if (!checkInWebRequest()) return null;
        setResponseMessage(status, message);
        try {
            HttpServletResponse response = getResponse();
            if (response != null && !response.isCommitted()) {
                response.setCharacterEncoding("UTF-8");
                response.setContentType("application/json; charset=UTF-8");
                response.setStatus(ResponseCode.getResponseCode(status).getResponseCode());
                String json = responseMessageThreadLocal.get().toJSON();
                try (OutputStream outputStream = response.getOutputStream()) {
                    outputStream.write(json.getBytes(StandardCharsets.UTF_8));
                    outputStream.flush();
                }
            }
        } catch (IOException ignore) {
        }
        return null;
    }

    public static <T> T response(ResponseCode responseCode, Object... args) {
        return response(responseCode.getStatusCode(), Transformation.lambdaTryCatch(() -> String.format(responseCode.getError(), args), responseCode::getError));
    }

    public static <T> T response401(String message) {
        return response(401, message);
    }
}
